<!DOCTYPE html> <!-- -*- html -*- -->
<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="initial-scale=1, maximum-scale=1">
  <title>Composing 2x2 Transformations</title>
  <link rel="shortcut icon" href="img/gatech.gif"/>

  
      <link rel="stylesheet" href="css/demo.css?vers=2759ff">
  

  <style>
      
  </style>

</head>
<body>
    

    
        <script src="js/demo.js?vers=77646a"></script>
    

    <script type="text/javascript">
        "use strict";
        DomReady.ready(function() {

        var color1, color2, color3, ortho,
  bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };

ortho = 10000;

color1 = new Color("green");

color2 = new Color("violet");

color3 = new Color("red");

window.demo = new Demo2D({
  ortho: ortho,
  camera: {
    position: [2.2, 0, ortho],
    lookAt: [2.2, 0, 0]
  },
  vertical: 3.0
}, function() {
  var Params, clipCube2, clipCube3, cols, computeOut, computeProduct, folder, folderNames, folders, gridData, gui, hiliteOpts, i, inverseNames, j, labelOpts, makeSurface, matrices, matrix1, matrix1Elt, matrix2, matrix2Elt, matrix3, matrix3Elt, names, params, paramses, range, s, strip, transforms, updateCaption, vector1, vector2, vector3, vectorOpts, view1, view2, view3, zeroOpts;
  window.mathbox = this.mathbox;
  range = this.urlParams.get('range', 'float', 3);
  transforms = this.urlParams.get('names', 'str[]', ['T', 'U']);
  strip = function(s) {
    return s.replace(/^\s+|\s+$/g, '');
  };
  names = (function() {
    var j, len, results;
    results = [];
    for (j = 0, len = transforms.length; j < len; j++) {
      s = transforms[j];
      results.push(strip(s));
    }
    return results;
  })();
  vector1 = this.urlParams.get('vec', 'float[]', [1, 2]);
  vector2 = [0, 0];
  vector3 = [0, 0];
  matrix1 = this.urlParams.get('mat1', 'float[]', [1, 0, 0, 1]);
  matrix2 = this.urlParams.get('mat2', 'float[]', [1, 0, 0, 1]);
  matrix3 = [0, 0, 0, 0];
  computeProduct = (function(_this) {
    return function() {
      matrix3[0] = matrix2[0] * matrix1[0] + matrix2[1] * matrix1[2];
      matrix3[1] = matrix2[0] * matrix1[1] + matrix2[1] * matrix1[3];
      matrix3[2] = matrix2[2] * matrix1[0] + matrix2[3] * matrix1[2];
      matrix3[3] = matrix2[2] * matrix1[1] + matrix2[3] * matrix1[3];
      return computeOut();
    };
  })(this);
  matrices = [matrix1, matrix2, matrix3];
  computeOut = function() {
    vector2[0] = matrix1[0] * vector1[0] + matrix1[1] * vector1[1];
    vector2[1] = matrix1[2] * vector1[0] + matrix1[3] * vector1[1];
    vector3[0] = matrix2[0] * vector2[0] + matrix2[1] * vector2[1];
    vector3[1] = matrix2[2] * vector2[0] + matrix2[3] * vector2[1];
    return updateCaption();
  };
  Params = (function() {
    function Params(matrix, inverseName) {
      this.doRotate = bind(this.doRotate, this);
      this.doYShear = bind(this.doYShear, this);
      this.doXShear = bind(this.doXShear, this);
      this.doScale = bind(this.doScale, this);
      this.xscale = 1.0;
      this.yscale = 1.0;
      this.xshear = 0.0;
      this.yshear = 0.0;
      this.rotate = 0.0;
      this.matrix = matrix;
      this[inverseName] = (function(_this) {
        return function() {
          var det;
          det = _this.other[0] * _this.other[3] - _this.other[1] * _this.other[2];
          if (Math.abs(det) < .00001) {
            window.alert("Matrix is not invertible!");
            return;
          }
          _this.xscale = _this.yscale = 1.0;
          _this.xshear = _this.yshear = _this.rotate = 0.0;
          _this.matrix[0] = _this.other[3] / det;
          _this.matrix[1] = -_this.other[1] / det;
          _this.matrix[2] = -_this.other[2] / det;
          _this.matrix[3] = _this.other[0] / det;
          return computeProduct();
        };
      })(this);
      this['show grid'] = false;
    }

    Params.prototype.doScale = function() {
      this.rotate = this.xshear = this.yshear = 0.0;
      this.matrix[0] = this.xscale;
      this.matrix[1] = this.matrix[2] = 0;
      this.matrix[3] = this.yscale;
      return computeProduct();
    };

    Params.prototype.doXShear = function() {
      this.xscale = this.yscale = 1.0;
      this.rotate = this.yshear = 0.0;
      this.matrix[0] = this.matrix[3] = 1;
      this.matrix[1] = this.xshear;
      this.matrix[2] = 0;
      return computeProduct();
    };

    Params.prototype.doYShear = function() {
      this.xscale = this.yscale = 1.0;
      this.rotate = this.xshear = 0.0;
      this.matrix[0] = this.matrix[3] = 1;
      this.matrix[1] = 0;
      this.matrix[2] = this.yshear;
      return computeProduct();
    };

    Params.prototype.doRotate = function() {
      var c;
      this.xscale = this.yscale = 1.0;
      this.xshear = this.yshear = 0.0;
      c = Math.cos(this.rotate);
      s = Math.sin(this.rotate);
      this.matrix[0] = this.matrix[3] = c;
      this.matrix[1] = -s;
      this.matrix[2] = s;
      return computeProduct();
    };

    return Params;

  })();
  paramses = [];
  gui = new dat.GUI();
  gui.closed = this.urlParams.closed != null;
  folderNames = [transforms[1], transforms[0]];
  inverseNames = [transforms[0] + " inverse", transforms[1] + " inverse"];
  folders = [];
  for (i = j = 0; j < 2; i = ++j) {
    folder = gui.addFolder(folderNames[i]);
    params = new Params(matrices[i], inverseNames[i]);
    folder.open();
    folder.add(params, 'xscale', -2, 2).step(0.05).onChange(params.doScale).listen();
    folder.add(params, 'yscale', -2, 2).step(0.05).onChange(params.doScale).listen();
    folder.add(params, 'rotate', -π, π).step(0.1 * π).onChange(params.doRotate).listen();
    folder.add(params, 'xshear', -2, 2).step(0.05).onChange(params.doXShear).listen();
    folder.add(params, 'yshear', -2, 2).step(0.05).onChange(params.doYShear).listen();
    folder.add(params, inverseNames[i]);
    params['show grid'] = urlParams["show" + (i + 1)] != null ? true : false;
    folder.add(params, 'show grid').onFinishChange((function(i) {
      return function(val) {
        return mathbox.select(".grid" + (i + 1)).set('visible', val);
      };
    })(i));
    paramses.push(params);
    folders.push(folder);
  }
  paramses[0].other = paramses[1].matrix;
  paramses[1].other = paramses[0].matrix;
  view1 = this.view({
    name: 'view1',
    axisLabels: false,
    grid: false,
    viewRange: [[-range, range], [-range, range]]
  });
  view2 = this.view({
    name: 'view2',
    axisLabels: false,
    grid: false,
    viewOpts: {
      position: [2.2, 0, 0]
    },
    viewRange: [[-range, range], [-range, range]]
  });
  view3 = this.view({
    name: 'view3',
    axisLabels: false,
    grid: false,
    viewOpts: {
      position: [4.4, 0, 0]
    },
    viewRange: [[-range, range], [-range, range]]
  });
  clipCube2 = this.clipCube(view2, {
    draw: false,
    hilite: false,
    range: range,
    pass: 'view'
  });
  clipCube3 = this.clipCube(view3, {
    draw: false,
    hilite: false,
    range: range,
    pass: 'view'
  });
  gridData = mathbox.area({
    width: 11,
    height: 11,
    channels: 2,
    rangeX: [-range, range],
    rangeY: [-range, range]
  });
  makeSurface = function(view, matrix, color, klass) {
    return view.transform({}, {
      matrix: function() {
        return [matrix[0], matrix[1], 0, 0, matrix[2], matrix[3], 0, 0, 0, 0, 1, 0, 0, 0, 0, 1];
      }
    }).surface({
      color: color.arr(),
      points: gridData,
      fill: false,
      lineX: true,
      lineY: true,
      width: 2,
      opacity: 0.5,
      zOrder: 0,
      classes: [klass]
    });
  };
  makeSurface(view1, [1, 0, 0, 1], color1, 'grid1');
  makeSurface(clipCube2.clipped, [1, 0, 0, 1], color2, 'grid2');
  makeSurface(clipCube2.clipped, matrix1, color1.darken(.15), 'grid1');
  makeSurface(clipCube3.clipped, matrix2, color2.darken(.15), 'grid2');
  makeSurface(clipCube3.clipped, matrix3, color1.darken(.3), 'grid1');
  mathbox.select('.grid1').set('visible', paramses[0]['show grid']);
  mathbox.select('.grid2').set('visible', paramses[1]['show grid']);
  vectorOpts = {
    size: 4,
    width: 3,
    zIndex: 2
  };
  zeroOpts = {
    zIndex: 2,
    size: 15
  };
  labelOpts = {
    offset: [0, 25],
    size: 15,
    zIndex: 3,
    outline: 0
  };
  hiliteOpts = {
    zTest: true,
    zWrite: true,
    zOrder: 2,
    opacity: 0.5
  };
  this.labeledVectors(view1, {
    name: 'labeled1',
    vectors: [vector1],
    colors: [color1],
    labels: ['x'],
    live: true,
    zeroPoints: true,
    zeroThreshold: 0.3,
    vectorOpts: vectorOpts,
    labelOpts: labelOpts,
    zeroOpts: zeroOpts
  });
  this.labeledVectors(view2, {
    name: 'labeled2',
    vectors: [vector2],
    colors: [color2],
    labels: [names[1] + "(x)"],
    live: true,
    zeroPoints: true,
    zeroThreshold: 0.3,
    vectorOpts: vectorOpts,
    labelOpts: labelOpts,
    zeroOpts: zeroOpts
  });
  this.labeledVectors(view3, {
    name: 'labeled3',
    vectors: [vector3],
    colors: [color3],
    labels: [names[0] + "(" + names[1] + "(x))"],
    live: true,
    zeroPoints: true,
    zeroThreshold: 0.3,
    vectorOpts: vectorOpts,
    labelOpts: labelOpts,
    zeroOpts: zeroOpts
  });
  this.draggable(view1, {
    name: 'drag1',
    points: [vector1],
    postDrag: computeOut,
    hiliteOpts: hiliteOpts
  });
  this.caption('<p><span id="matrix1-here"></span></p>\n<p><span id="matrix2-here"></span></p>\n<p><span id="matrix3-here"></span></p>');
  matrix1Elt = document.getElementById('matrix1-here');
  matrix2Elt = document.getElementById('matrix2-here');
  matrix3Elt = document.getElementById('matrix3-here');
  cols = function(mat) {
    return [[mat[0], mat[2]], [mat[1], mat[3]]];
  };
  updateCaption = (function(_this) {
    return function() {
      var str;
      str = names[1] + "(\\color{" + (color1.str()) + "}{x}) = ";
      str += _this.texMatrix(cols(matrix1));
      str += _this.texVector(vector1, {
        color: color1.str()
      });
      str += "=";
      str += _this.texVector(vector2, {
        color: color2.str()
      });
      katex.render(str, matrix1Elt);
      str = names[0] + "(\\color{" + (color2.str()) + "}{" + names[1] + "(x)}) = ";
      str += _this.texMatrix(cols(matrix2));
      str += _this.texVector(vector2, {
        color: color2.str()
      });
      str += "=";
      str += _this.texVector(vector3, {
        color: color3.str()
      });
      katex.render(str, matrix2Elt);
      str = names[0] + "\\circ " + names[1] + "(\\color{" + (color1.str()) + "}{x}) = ";
      str += _this.texMatrix(cols(matrix3));
      str += _this.texVector(vector1, {
        color: color1.str()
      });
      str += "=";
      str += _this.texVector(vector3, {
        color: color3.str()
      });
      return katex.render(str, matrix3Elt);
    };
  })(this);
  return computeProduct();
});


        });
    </script>
</body>
</html>

